/**
 * com.zpyr.web
 * 
 * @author BJS aka Zephyr
 * @version 0.5
 * @date 2012-03-08
 * @requires jquery , com.zpyr.js
 * 
 * com.zpyr.web.Hist
 * com.zpyr.web.Navigation @require navigation.css
 * com.zpyr.web.DragDrop
 */

Module.createNamespace("com.zpyr.web", "1.0");

/**
 * com.zpyr.web.Hist : AJAX 페이지 이동시 히스토리 관리.
 * 
 * Ajax 를 이용하여 페이지를 이동하면 브라우저의 뒤로가기 버튼을 이용할 수 없다.
 * Ajax 로 페이지 전체를 리로딩하지 않으면서, 브라우저의 뒤로가기 버튼을 이용하기 위해
 * HTML5의 pushState를 사용.

 * 
 * ** 특이사항 **
 * 1. 이동할 페이지를 HTML Anchor(#) 을 이용하여 구현하였음.
 *    새로고침을 해도 현재 URL 을 표시하기 위하여
 *    onpageshow 이벤트에서 Anchor 값을 구분하여 페이지를 전환한다.
 * 
 * 2. Anchor 를 이용하였기 때문에 뒤로가기,앞으로가기를 사용하기 위해서
 *    onhashchange 이벤트를 사용하였다.
 * 3. 브라우저 history를 사용할 수 없는 ajax 페이지 이동에만 사용한다.
 * *************
 * 
 * ** 사용법 **
 * 1. history 에 pageId 와 url 을 등록한다.
 * 2. 실제로 페이지 전환시에 go(pageId , e)를 사용한다. (history 에 이미 등록된 page 만 사용가능).
 * ***********
 */

com.zpyr.web.Hist = function() {
	
	this.pageList = new com.zpyr.data.HashMap();
	var _this = this;
	
	window.onhashchange = function(e) {
	//	var thisUrl = e.newURL;
	//	var pageId = _this.getPageId(thisUrl);
		var pageId = location.hash.substring(1);
		_this.ajaxGoUrl(pageId);
		_this.hashChangeCB();
	};
	
	window.onload = function(e) {
		// e.srcElement 파이어폭스에서 안됨.
		// e.target 크롬 , 파이어폭스 지원.
	//	var thisUrl = e.target.URL;
	//	var pageId = _this.getPageId(thisUrl);
		var pageId = location.hash.substring(1);
		_this.ajaxGoUrl(pageId);
		_this.pageShowCB();
	};
	
};

com.zpyr.web.Hist.prototype = {
		pageList : null ,
		targetDiv : "" ,
		
		/**
		 * Hist 에 페이지를 등록한다.
		 * 등록하는 것으로는 아무런 효과가 없고, 
		 * 이벤트 발생시 go 을 사용하여 등록된 pageId를 사용한다.
		 * 
		 * @param pageId : 등록된 키값. pageId값은 실제 url 에 #값으로 사용된다.
		 * @param url    : 키값을 사용하였을때 실제로 내용을 보여줄 url
		 * @param -- optional
		 * @param cbFunc : url 로의 이동이 끝난 후 실행할 콜백함수
		 * @param targetDiv : 내용을 보여줄 divId. 이 값이 없으면 그냥 실행만 하고 페이지에는 변화가 없다.
		 */
		addPage : function( pageId , url , cbFunc , targetDiv ) {
			if(!targetDiv) targetDiv = this.targetDiv;
			this.pageList.put(pageId, { 'url' : url , 'targetDiv' : targetDiv , 'cbFunc' : cbFunc });
		} ,
		setTargetDiv : function( targetDiv ) {
			this.targetDiv = targetDiv;
		} ,
		/**
		 * 실제로 page를 이동한다.
		 * @param pageId : 이미 등록된 pageId값
		 * @param -- optional
		 * @param e : 발생한 이벤트
		 */
		go : function( pageId , params , e ) {
			if(e) e.preventDefault();
			if(history.pushState) {
				history.pushState(pageId , 'title'+pageId , '#'+pageId);
			}
			else {
				// ie 미지원.
				window.location.hash = '#' + pageId;
			}
			
			this.ajaxGoUrl(pageId , params);
		} , 
		ajaxGoUrl : function(pageId , params) {
			var pageInfo = this.pageList.get(pageId);
			var _this = this;
			if(pageInfo) {
				$.post( pageInfo.url , params , function(data) {
					if( pageInfo.targetDiv ) {
						$('#' + pageInfo.targetDiv).html(data);
					}
					else if( _this.targetDiv ) {
						$('#' + _this.targetDiv).html(data);
					}
					if( pageInfo.cbFunc ) pageInfo.cbFunc(data);
				} );
			}
		} ,
		getPageId : function(thisUrl) {
			var idx = thisUrl.lastIndexOf("#");
			var pageId = "";
			if(idx != -1) {
				pageId = thisUrl.substr( idx+1 );
			}
			return pageId;
		} ,
		hashChangeCB : function() {
			
		} ,
		pageShowCB : function() {
			
		}
};


////////////////////////////////////////


/**
 * HTML5 드래그 드롭.
 * 드래그할 엘리먼트들(A)과 드롭대상 엘리먼트들(B)을 지정하여,
 * A가 B 위에 올라가면 A를 B의 제일 마지막에 넣는다.
 *  
 * javascript:HashMap 객체가 반드시 필요함.
 * 2012-03-05일 현재, 크롬 , 파이어폭스에서만 지원.
 * 크롬에서는 e.dataTransfer.getData('text') <- 여기서 text 대신 다른 값은 인식하지 못하는 버그가 있음.
 * 크롬에서 e.dataTransfer.getData('text') 를 제대로 지원하지 않아, 이동중에 다른 효과를 주는 것은 미적용.
 * 향후 개선방안.
 * 1. 크롬에서 지원한다면 dragOverHandler 를 개선하도록.
 * 2. 현재는 드래그 해서 무조건 뒤로 집어넣는데, 마우스 좌표에 따라 앞에 들어갈 수도 있어야 한다.
 * 3. 지금은 드래그 해서 드롭타켓에 집어넣는 방식인데, 자유롭게 좌표에 따라 이동하는 방식도 추가.
 * 
 * @author BJS aka Zephyr
 * @version 0.5
 * @date 2012-03-08
 */
com.zpyr.web.DragDrop = function(mode) {
	this.dragElementList = new HashMap();
	this.dropTargetList  = new HashMap();
	this.dragModes = ['box' , 'free'];
	mode = mode || 0;
	this.dragMode = this.dragModes[mode];
	this.dragInfo = { 'x' : 0 , 'y' : 0 , clear : function(){ this.x=0; this.y=0; }};
	
};

com.zpyr.web.DragDrop.prototype = {
	/**
	 * 드래그할 element 를 등록한다
	 * 
	 * @param    dragElementId : 실제로 드래그 되어 움직일 대상의 ID값.(유일해야 하므로 ID)
	 * @param    dragHandlerSelector : 위 대상을 드래그 하기 위한 영역을 querySelector 로 지정.
	 * @param -- dragHandlerSelector를 마우스로 끌면 dragElementId를 이동하는 방식으로 동작함.
	 * @param -- dragHandlerSelector 가 없으면 dragElementId 가 자체를 끌어서 이동하게 된다.
	 * 
	 * 
	 */
	addDragELement : function(dragElementId , dragHandlerSelector) {
		var dragElement = document.querySelector('#'+dragElementId);
		var dragHandler;
		
		
		if(dragElement) {
			dragHandler = dragElement.querySelector(dragHandlerSelector);
			if( dragHandler ) {
				this.dragElementList.put(dragElementId, { element : dragElementId , handler : '#' + dragElementId + ' ' + dragHandlerSelector });
				$( dragHandler ).css('cursor' , 'move');
			}
			else {
				this.dragElementList.put(dragElementId, { element : dragElementId , handler : '#' + dragElementId });
				$( dragElement ).css('cursor' , 'move');
			}
		}
	},
	/**
	 * 아이템들이 드랍될 element를 등록한다.
	 * @param id
	 */
	addDropTarget : function(id , selector) {
		var dropElement;
		if(typeof selector == 'object') dropElement = selector.get(0);
		else dropElement = document.querySelector( selector );
		
		if(dropElement) {
			this.dropTargetList.put(id, dropElement);
		}
	},
	/**
	 * 해당 element의 드래그드롭을 중지한다.
	 * @param id
	 */
	remove: function(id) {
		this.dragElementList.remove(id);
		this.dropTargetList.remove(id);
	},
	/**
	 * 드래그 드롭을 시작한다.
	 */
	run: function() {
		var key;
		for( var idx in this.dragElementList.keySet() ) {
			key = this.dragElementList.keySet()[idx];
			this.draggable( key , this.dragElementList.get( key ) );
		}
		for( var idx in this.dropTargetList.keySet() ) {
			key = this.dropTargetList.keySet()[idx];
			this.dropable( key , this.dropTargetList.get( key ) );
		}
	},

	draggable: function(id , element) {
		var _this = this;
		var dragElement = $(element.handler);
		if(dragElement) {
			dragElement.attr('draggable' , 'true');

			dragElement.get(0).addEventListener('dragstart' , function(e){
				_this.dragstartHandler(e , id);
				_this.dragInfo.x = e.pageX;
				_this.dragInfo.y = e.pageY;
			});
		}
	},
	dropable: function(id , element) {
		var _this = this;
		if(element) {
			element.addEventListener('dragover' , function(e){
				_this.dragoverHandler(e , _this);
			} , false);
			element.addEventListener('drop' , function(e) {
				// 드래그 모드가 box 형태 이면 드롭핸들러를 적용하여 타겟을 찾아 드롭한다.
				// 드래그 모드가 free 이면 드롭시에는 아무것도 하지 않는다.
				
				if( _this.dragMode == _this.dragModes[0] ) _this.dropHandler(e, element, _this);
				else if( _this.dragMode == _this.dragModes[1] ) _this.dropHandlerFree(e, element, _this) ;
				
			} , false);
		}
	},
	/**
	 * dragStart 시 이벤트 핸들러.
	 * 
	 * @param e
	 * @param id
	 */
	dragstartHandler: function(e , id) {
		var dragElement = $('#' + id);
		e.dataTransfer.setData('text' , id  );
		console.log(1);
	},
	dragoverHandler: function(e , _this) {
		
		if( _this.dragElementList.containsKey( e.dataTransfer.getData('text') ) ) {
			e.preventDefault(); // 크롬에서는 되지 않는다.
		}
		
		e.preventDefault();
	},
	dropHandler: function(e , element , _this) {
		
		var dragElementId = e.dataTransfer.getData('text'); // 드래그 중인 엘리먼트ID
		
		var mx = e.pageX; // 마우스 x 좌표
		var my = e.pageY; // 마우스 y 좌표
		var dragElement = null; // 드래그 중인 엘리먼트
		
		if( _this.dragElementList.containsKey( dragElementId ) ) {
			if (e.stopPropagation) e.stopPropagation(); // stops the browser from redirecting...why???
			dragElement = $( '#' +  dragElementId);
		}
		if(dragElement == null) return; // 드래그 엘리먼트를 찾아서 있으면 진행하고, 없으면 취소.
		
		/*
		 드래그 엘리먼트의 위치 및 드롭타겟 엘리먼트의 위치를 판단하여 적절한 곳에 드랍하고 순서를 정렬한다.
		 */
		var elemId , elem , bx , by; // -1 : 왼쪽&위 , 0 : 내부 , 1 : 오른쪽&아래
		for( var idx in _this.dragElementList.keySet() ) {
			elemId = _this.dragElementList.keySet()[idx];
			elem = $('#' + elemId);
			
			if( dragElementId == elemId ) {
				continue;
			}
			
			bx = -100 , by = -100;
			if( mx < elem.offset().left ) bx = -1; 
			else if( mx <= elem.offset().left + elem.width() ) bx = 0;
				
			else if( elem.offset().left + elem.width() < mx ) bx = 1;
			
			if( my < elem.offset().top ) by = -1;
				
			else if( my <= elem.offset().top + elem.height() ) by = 0;
				
			else if( elem.offset().top + elem.height() < my ) by = 1;
			
			var iElem;
			if( bx == 0 ) {
				if(by == 0 ) {
					dragElement.insertAfter( elem );
					return;
				}
				else if( by == -1 ) {
					dragElement.insertBefore( elem );
					return;
				}
				else if( by == 1 ) {
					iElem = elem;
					continue;
				}
			}
			if(iElem) dragElement.insertAfter( iElem );
			
		}
		
		for( var idx in _this.dropTargetList.keySet() ) {
			elemId = _this.dropTargetList.keySet()[idx];
			elem = $('#' + elemId);
			
			bx = -100 , by = -100;
			if( mx < elem.offset().left ) bx = -1; 
			else if( mx <= elem.offset().left + elem.width() ) bx = 0;
				
			else if( elem.offset().left + elem.width() < mx ) bx = 1;
			
			if( my < elem.offset().top ) by = -1;
				
			else if( my <= elem.offset().top + elem.height() ) by = 0;
				
			else if( elem.offset().top + elem.height() < my ) by = 1;
			
			if( bx == 0 && by == 0 ) {
				elem.append( dragElement );
				return;
			}
		}
	},
	dropHandlerFree: function(e , element , _this) {
		var dragElementId = e.dataTransfer.getData('text'); // 드래그 중인 엘리먼트ID
		var mx = e.pageX; // 마우스 x 좌표
		var my = e.pageY; // 마우스 y 좌표
		var dragElement = null; // 드래그 중인 엘리먼트
		
		if( _this.dragElementList.containsKey( dragElementId ) ) {
			if (e.stopPropagation) e.stopPropagation(); // stops the browser from redirecting...why???
			dragElement = $( '#' +  dragElementId);
		}
		if(dragElement == null) return; // 드래그 엘리먼트를 찾아서 있으면 진행하고, 없으면 취소.

		var movex = mx - _this.dragInfo.x;
		var movey = my - _this.dragInfo.y;
		
		dragElement.css('position' , 'absolute');
		dragElement.css('left' , dragElement.offset().left + (mx - _this.dragInfo.x) );
		dragElement.css('top' ,  dragElement.offset().top  + (my - _this.dragInfo.y) );
		
	//	_this.dragInfo.clear();
		
		
	}
};

/**
 * 
 * @param width
 * @param height
 * @param title   : 제목
 * @param content : url
 * @returns {com.zpyr.web.Popup}
 */
com.zpyr.web.Popup = function(width , height , top , left , title , content_url) {
	var now = new Date();	var h = now.getHours();	var m = now.getMinutes();	var s = now.getSeconds();	var y = now.getFullYear();	var mon = now.getMonth() + 1;	var d = now.getDate();
	if(m <= 9) m = '0' + m;	if(s <= 9) s = '0' + s;	if(mon <= 9) mon = '0' + mon;	if(d <= 9) d = '0' + d;
	
	this.gen_id = 'popup' + y + mon + d + h + m + s;
	
	var title_div;
	if(title)   title_div = '<div name="title" style="text-align:center; padding:5px;">' + title 
						  + '<span name="closer" style="float:right; margin-right:5px; cursor:pointer; font-weight: bold; font-size:14px;">X</span>'
						  + '</div>';
	
	var content_div = $('<div name="content">');
	
	if(!left) left = 0;
	if(!top) top = 0;
	if(!width) width = 0;
	if(!height) height = 0;
	
	var def_css = { 'position' : 'absolute', 
			  'left' : left,
			  'top' : top,
			  'width' : width,
			  'height' : height,
			  'border' : '1px solid gray',
			  'z-index' : '2000',
			  'background' : '#ffffff',
			  };
	
	this.element = $('<div>').attr('id' , this.gen_id).css(def_css);
	if(title_div) this.element.html(title_div);
	
	this.element.append(content_div);
	
	var _this = this;
	this.element.find('span[name=closer]').click(function(){
		_this.close();
	});
	
	$('body').append(this.element);
	this.hide();
	
	$.post(content_url , {} , function(data){
		content_div.html(data);
		
	});
	
};

com.zpyr.web.Popup.prototype = {
	setCss : function(opt) {
		this.element.css(opt);
	},
	setDrag : function(bool) {
		if(bool) {
			var d = new com.zpyr.web.DragDrop(1);
			d.addDragELement(this.gen_id, ' div[name=title]');
			d.addDropTarget('drop_target_all' , $(document));
			d.run();
		}
	},
	show : function() {
		this.element.show();
	},
	hide : function() {
		this.element.hide();
	},
	remove : function() {
		this.element.remove();
	},
	close : function() {
		this.remove();
	},
	setX : function(x) {
		this.element.css('left' , x);
	},
	setY : function(y) {
		this.element.css('top' , y);
	}
};



/**
 * 
 * 드래그 드롭2.
 * 
 * 사용방법.
 * 
 * var a = com.zpyr.web.DragDrop2();
 * a.addDragElement( dragElementSelector , dragHandlerSelector );
 * a.addDropTarget( dropTargetSelector ); // if param is null -> target is body element.
 * a.run();
 *  
 * jquery 만을 사용.
 * 
 * 
 */

com.zpyr.web.DragDrop2 = function() {
	
	this.dragElementList = new HashMap();
	this.dropTargetList  = new HashMap();
	
	this.dragMode = this.dragModes[mode];
	this.dragInfo = {
		id : '' ,
		'x' : 0 , 
		'y' : 0 , 
		clear : function(){ 
			this.x=0; this.y=0; 
		},
	};
	
};

com.zpyr.web.DragDrop2.prototype = {
	
	addDragElement: function( dragElementSelector , dragHandlerSelector ) {
		
		var _this = this;
		
		var dragElement = $(dragElementSelector);
		var dragHandler = $(dragHandlerSelector);
		
		if(!dragElement.get(0)) {
			console.log('web.DragDrop2.addDragElement() : '+ dragElementSelector + '에 해당하는 엘리먼트가 없습니다.');
			return;
		}
		
		if(!dragHandler.get(0)) {
			dragHandler = dragElement;
		}
		
		dragElement.each(function() {
			
			_this.dragElementList.put(
					dragElementSelector, { element : dragElementSelector , handler : dragHandlerSelector });
			$( dragHandler ).css('cursor' , 'move');
		});
		
	},
	
	addDropTarget : function( dropTargetSelector ) {
		var dropElement = $(dropTargetSelector);
		if(!dropElement.get(0)) {
			console.log('web.DragDrop2.addDropTarget() : '+ dropTargetSelector + '에 해당하는 엘리먼트가 없습니다.');
			return;
		}
		
		this.dropTargetList.put(dropTargetSelector, dropTargetSelector);
	},
	
	run : function() {
		
	},
	
	
}















/**
 * formId 값을 넘긴다.
 * 
 * 각 input의 타입에 따라 빈값을 체크하거나, 항목을 체크한다.
 * input 에 값을 넣을때마다 체크하기도 하고,
 * form 의 submit 을 실행할때도 최종체크 해야한다.
 * 
 * @param formId
 * @returns {com.zpyr.web.MakeForm}
 */
com.zpyr.web.MakeForm = function(formId) {
	
	this.formElement = $('#' + formId);
	
	this.nulCheck();
	
};

com.zpyr.web.MakeForm.prototype = {
	nulCheck : function() {
		this.formElement.find(':text').each(function(){
			var textElem = $(this);
			textElem.change(function(){
				if( textElem.val() == '' ) {
					
				}
			});
		})
	},
};